import numpy as np
import pandas as pd
import seaborn as sns



# topsis.py
import numpy as np
import pandas as pd

# ---------------- 1. 正向化函数 ----------------
def min2max(x: np.ndarray) -> np.ndarray:
    """极小型 -> 极大型"""
    return x.max() - x

def mid2max(x: np.ndarray, best: float) -> np.ndarray:
    """中间型 -> 极大型"""
    M = np.abs(x - best).max()
    return 1 - np.abs(x - best) / M

def inter2max(x: np.ndarray, a: float, b: float) -> np.ndarray:
    """区间型 -> 极大型"""
    M = max(a - x.min(), x.max() - b)
    res = np.ones_like(x, dtype=float)
    res[x < a] = 1 - (a - x[x < a]) / M
    res[x > b] = 1 - (x[x > b] - b) / M
    return res

# ---------------- 2. 正向化调度器 ----------------
def positivize(X: np.ndarray, types: list) -> np.ndarray:
    """
    X: shape=(n,m) 原始决策矩阵
    types: list of int/str, 每列的指标类型
           1 / 'max'  : 极大型（无需处理）
           2 / 'min'  : 极小型
           3 / 'mid'  : 中间型，需提供 best 值
           4 / 'inter': 区间型，需提供 [a, b]
    返回正向化后的矩阵
    """
    X = X.astype(float).copy()
    n, m = X.shape
    assert len(types) == m, "types 长度需与列数相等"

    for j, tp in enumerate(types):
        col = X[:, j]
        if tp in (1, 'max'):
            continue
        elif tp in (2, 'min'):
            X[:, j] = min2max(col)
        elif isinstance(tp, tuple) and tp[0] == 3:
            best = tp[1]
            X[:, j] = mid2max(col, best)
        elif isinstance(tp, tuple) and tp[0] == 4:
            a, b = tp[1], tp[2]
            X[:, j] = inter2max(col, a, b)
        else:
            raise ValueError(f"未知类型：{tp}")
    return X

# ---------------- 3. TOPSIS 主函数 ----------------
def topsis(X: np.ndarray,
           types: list,
           weights: np.ndarray = None) -> pd.DataFrame:
    """
    返回 DataFrame：['Score', 'Rank']
    """
    # 1) 正向化
    X_pos = positivize(X, types)

    # 2) 标准化（除以列向量的 L2 范数）
    Z = X_pos / np.sqrt((X_pos ** 2).sum(axis=0))

    # 3) 加权
    if weights is None:
        weights = np.ones(Z.shape[1]) / Z.shape[1]
    Z = Z * weights

    # 4) 理想解 / 负理想解
    Z_max = Z.max(axis=0)
    Z_min = Z.min(axis=0)

    # 5) 距离
    D_plus = np.sqrt(((Z - Z_max) ** 2).sum(axis=1))
    D_minus = np.sqrt(((Z - Z_min) ** 2).sum(axis=1))

    # 6) 相对贴近度
    C = D_minus / (D_plus + D_minus)

    # 7) 排名
    df = pd.DataFrame({'Score': C})
    df['Rank'] = df['Score'].rank(method='min', ascending=False).astype(int)
    return df

# ---------------- 4. Demo ----------------
if __name__ == "__main__":
    # 明星选对象示例
    X_demo = np.array([
        [9, 10, 175, 120],
        [8, 7, 164, 80],
        [6, 3, 157, 90]
    ])
    # 指标类型：极大型, 极小型, 中间型(165), 区间型(90,100)
    types_demo = [1, 2, (3, 165), (4, 90, 100)]
    res = topsis(X_demo, types_demo)
    print("示例结果：")
    print(res)
    